package agents;

import jade.core.AID;
import jade.core.Agent;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.FailureException;
import jade.domain.FIPAAgentManagement.NotUnderstoodException;
import jade.domain.FIPAAgentManagement.RefuseException;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.proto.AchieveREInitiator;
import jade.proto.AchieveREResponder;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import building.AuxInfo;
import building.Cell;
import building.InfoGame;
import evacuator.MovementRely;

/**
 * <p>
 * <B>Title:</b> Fire Evacuation
 * </p>
 * 
 * @author Carolina Arce & Caterina Bruno
 * @see agents.CentralAgent
 */
public class CoordinatorAgent extends Agent {

	/**
     *
     */
	private static final long serialVersionUID = 2444910571881207948L;

	private AuxInfo info;

	private AID centralAgent;

	ArrayList<AID> evacuatorAgentsList;
	private AID firemenManager;
	private AID paramedic;

	public CoordinatorAgent() {
	}

	/**
	 * A message is shown in the log area of the GUI
	 * 
	 * @param str
	 *            String to show
	 */
	private void showMessage(String str) {
		System.out.println(getLocalName() + ": " + str);
	}

	/**
	 * Agent setup method - called when it first come on-line. Configuration of
	 * language to use and initialization of behaviors.
	 */
	protected void setup() {

		/**** Very Important Line (VIL) *********/
		this.setEnabledO2ACommunication(true, 1);
		/****************************************/

		// Register the agent to the DF
		ServiceDescription sd1 = new ServiceDescription();
		sd1.setType(UtilsAgents.COORDINATOR_AGENT);
		sd1.setName(getLocalName());
		sd1.setOwnership(UtilsAgents.OWNER);
		DFAgentDescription dfd = new DFAgentDescription();
		dfd.addServices(sd1);
		dfd.setName(getAID());
		try {
			DFService.register(this, dfd);
			showMessage("Registered to the DF");
		} catch (FIPAException e) {
			System.err.println(getLocalName() + " registration with DF "
					+ "unsucceeded. Reason: " + e.getMessage());
			doDelete();
		}

		// we search the CentralAgent
		ServiceDescription searchCriterion = new ServiceDescription();
		searchCriterion.setType(UtilsAgents.CENTRAL_AGENT);
		this.centralAgent = UtilsAgents.searchAgent(this, searchCriterion);

		// we search for manager Agents
		searchCriterion.setType(UtilsAgents.EVACUATOR_AGENT);
		this.evacuatorAgentsList = UtilsAgents.searchAgentS(this,
				searchCriterion);
		System.out.println("evacuators trovati" + evacuatorAgentsList.size());

		// we search for paramedic agent
		// searchCriterion.setType(UtilsAgents.PARAMEDIC_AGENT);
		// this.paramedic = UtilsAgents.searchAgentS(this,
		// searchCriterion).get(0);
		// System.out.println("paramedico trovato "+ paramedic.getLocalName());

		// we search for fireman Agents
		// searchCriterion.setType(UtilsAgents.FIREMENMANAGER);
		// this.firemenManager= UtilsAgents.searchAgent(this, searchCriterion);
		// System.out.println("firemenManager trovato " +
		// this.firemenManager.getLocalName());

		// add a behavior to receive end turns and movement orders
		this.addBehaviour(new MessageReceiver(this, null));
		// add a behavior to rely movement orders to Central Agent
		new MovementRely().addBehavior(this, agents.UtilsAgents.CENTRAL_AGENT);

	} // endof setup

	class MessageReceiver extends AchieveREResponder {

		@Override
		protected ACLMessage prepareResultNotification(ACLMessage arg0,
				ACLMessage arg1) throws FailureException {
			return null;
		}

		@Override
		protected ACLMessage prepareResponse(ACLMessage arg0)
				throws NotUnderstoodException, RefuseException {
			/*
			 * method called when the message has been received. If the message
			 * to send is an AGREE the behaviour will continue with the method
			 * prepareResultNotification.
			 */

			ACLMessage reply = arg0.createReply();
			try {
				Object contentRebut = (Object) arg0.getContentObject();
				if (contentRebut instanceof InfoGame) {
					InfoGame game = (InfoGame) contentRebut;

					reply.setPerformative(ACLMessage.AGREE);
					showMessage("Coordinator Turn received: "
							+ game.getInfo().getTurn());

					// Send map to all evacuators
					ACLMessage requestInicial = new ACLMessage(
							ACLMessage.REQUEST);
					requestInicial.clearAllReceiver();
					System.out.println(evacuatorAgentsList.size()
							+ "evacuators found by the Coordinator");

					for (int i = 0; i < evacuatorAgentsList.size(); i++) {

						requestInicial.addReceiver(evacuatorAgentsList.get(i));
						System.err
						.println("COORDINATOR ADD RECEIVER "
								+ evacuatorAgentsList.get(i)
										.getLocalName());
					}
					
					
	/*				for (int i = 0; i < evacuatorAgentsList.size(); i++) {

						if (!game.isAgentRemoved(evacuatorAgentsList.get(i))) {
							System.err
									.println("COORDINATOR ADD RECEIVER "
											+ evacuatorAgentsList.get(i)
													.getLocalName());
							requestInicial.addReceiver(evacuatorAgentsList
									.get(i));
						}
					}
					if (agents.UtilsAgents.getEvacuatorsByFloor(game, 0).size() == 0) {
					
						// evacuatorAgentsList.get(0).
						Cell ev = agents.UtilsAgents.getEvacuatorsByFloor(game, 1).get(0);
						AID evac_aid = ev.getAgent().getAID();
						// invio il turno all'evacuator del piano 0
						for (int i = 0; i < evacuatorAgentsList.size(); i++) {
							if(!evacuatorAgentsList.get(i).equals(evac_aid)){
								System.out
								.println("Evacuator rimane in palazzo per inviare i turni"
										+ evacuatorAgentsList.get(i).getLocalName());
								requestInicial.addReceiver(evacuatorAgentsList.get(i));
							}
						
						
						
						}

					}*/

					//requestInicial.addReceiver(paramedic);
					// System.out.println(info.getNumEvacuators());
					/*
					 * if( game.getInfo().getNumEvacuators()*game.getNfloors()
					 * == game.getRemovedAgents().size()){
					 * requestInicial.addReceiver(evacuatorAgentsList.get(0));
					 * //un evacuator rimane per inviare i turni
					 * 
					 * }
					 */
					requestInicial.setProtocol(UtilsAgents.PROTOCOL_TURN);
					try {
						requestInicial.setContentObject(game);
					} catch (Exception e) {
						e.printStackTrace();
					}
					// we add a behavior that sends the message and waits for an
					// answer
					this.myAgent.addBehaviour(new MessageSender(this.myAgent,
							requestInicial));
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

			// Send the map to managers

			return reply;
		}

		/**
         *
         */
		private static final long serialVersionUID = -5733983393467734330L;

		public MessageReceiver(Agent arg0, MessageTemplate arg1) {
			super(arg0, arg1);
			// TODO Auto-generated constructor stub
		}

	}

	class MessageSender extends AchieveREInitiator {

		/**
         *
         */
		private static final long serialVersionUID = -8383729124578338150L;

		public MessageSender(Agent arg0, ACLMessage arg1) {
			super(arg0, arg1);
			// TODO Auto-generated constructor stub
		}

	}

	/*************************************************************************/

	/**
	 * <p>
	 * <B>Title:</b> Fire evacuation
	 * </p>
	 * This class lets send the REQUESTs for any agent. Concretely it is used in
	 * the initialization of the game. The Coordinator Agent sends a REQUEST for
	 * the information of the game (instance of <code>AuxInfo</code> containing
	 * parameters, coordinates of the agents and the fire positions and visual
	 * range of each agent). The Central Agent sends an AGREE and then it
	 * informs of this information which is stored by the Coordinator Agent. The
	 * game is processed by another behavior that we add after the INFORM has
	 * been processed.
	 * 
	 * @author Carolina Arce and Caterina Bruno
	 * @see building.Cell
	 * @see building.InfoGame
	 */
	class RequesterBehaviour extends AchieveREInitiator {

		/**
         *
         */
		private static final long serialVersionUID = 3102064470122523141L;
		@SuppressWarnings("unused")
		private ACLMessage msgSent = null;
		@SuppressWarnings("unused")
		private boolean finish = false;

		public RequesterBehaviour(Agent myAgent, ACLMessage requestMsg) {
			super(myAgent, requestMsg);
			showMessage("AchieveREInitiator starts...");
			msgSent = requestMsg;
		}

		/**
		 * Handle AGREE messages
		 * 
		 * @param msg
		 *            Message to handle
		 */
		protected void handleAgree(ACLMessage msg) {
			showMessage("AGREE received from "
					+ ((AID) msg.getSender()).getLocalName());
		}

		/**
		 * Handle INFORM messages
		 * 
		 * @param msg
		 *            Message
		 */
		@SuppressWarnings("unchecked")
		protected void handleInform(ACLMessage msg) {
			try {
				info = (AuxInfo) msg.getContentObject();
				if (info instanceof AuxInfo) {
					HashMap<?, ?> hm = info.getAgentsInitialPosition();
					Iterator<?> it = hm.keySet().iterator();
					while (it.hasNext()) {
						building.InfoAgent ia = (building.InfoAgent) it.next();
						java.util.List<Cell> pos = (java.util.List<Cell>) hm
								.get(ia);
						Iterator<Cell> it2 = pos.iterator();
						while (it2.hasNext()) {
							it2.next();
						}
					}
				}
			} catch (Exception e) {
				System.out
						.println(e.toString() + "Coordinator agent exception");
			}
		}

		/**
		 * Handle NOT-UNDERSTOOD messages
		 * 
		 * @param msg
		 *            Message
		 */
		protected void handleNotUnderstood(ACLMessage msg) {
			System.out
					.println("Coordinator agent exception handleNotUnderstood");
		}

		/**
		 * Handle FAILURE messages
		 * 
		 * @param msg
		 *            Message
		 */
		protected void handleFailure(ACLMessage msg) {
			System.out.println("Coordinator agent exception handleFailure");
		} // End of handleFailure

		/**
		 * Handle REFUSE messages
		 * 
		 * @param msg
		 *            Message
		 */
		protected void handleRefuse(ACLMessage msg) {
			System.out.println("Coordinator agent exception handleRefuse");
		}
	} // Endof class RequesterBehaviour

} // endof class CoordinatorAgent